/*
 * topological-hex, a program to compute combinatorial hexahedral meshes.
 *
 * Copyright (C) <2018> <Université catholique de Louvain (UCL), Belgique>
 *
 * List of the contributors to the development, description and complete
 * License: see LICENSE file.
 *
 * This program (topological-hex) is free software:
 * you can redistribute it and/or modify it under the terms
 * of the GNU General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program (see COPYING file).  If not,
 * see <http://www.gnu.org/licenses/>.
 */


#ifndef SOLVER_H_
#define SOLVER_H_

#include <stdint.h>
#include <stddef.h>

#include "types.h"
#include "vertex_set.h"
#include "mesh.h"

/**
 * @defgroup solver Solver
 *
 * Data structures and operations used to find all-hexahedral meshes
 * topologically, ignoring the coordinates of the vertices.
 */

/**
 * @addtogroup solver
 * @{
 */

struct solver;

/**
 * Function called when a solution is found.
 *
 * @param num_hexes Number of hexahedra in the solution
 * @param data The user_data pointer passed to @ref solver_run.
 */
typedef void (*solver_callback)(const struct solver *solver,
                                uint32_t num_hexes, void *user_data);

typedef enum solver_flags {
  /**
   * Only search for shellable meshes. This specialized version of the algorithm
   * is significantly faster.
   */
  SOLVER_SHELLABLE_ONLY  = (1 << 0),

  /**
   * Assume that the graph of the output mesh must be bipartite.
   *
   * For certain domains such as balls, this is always the case, and using this
   * property speeds up the search.
   */
  SOLVER_BIPARTITE_GRAPH = (1 << 1),
} solver_flags;

typedef struct solver {
  uint32_t num_vertices;
  uint32_t num_quads;
  uint32_t num_hexes;
  size_t num_branches;
  uint8_t flags;

  uint32_t max_quad_id, max_vertex_id;

  hex_index hex_id;

  ALIGNED vertex_index *quads;
  ALIGNED vertex_index *hexes;
  ALIGNED hex_index *hex_adjacency;
  ALIGNED quad_index *hex_facets;

  /* Field only maintained for the shellable solver. */
  ALIGNED quad_edge *quad_adjacency;

  uint32_t quad_queue_size;
  uint32_t quad_queue_offset;
  ALIGNED quad_index *quad_queue;
  ALIGNED uint32_t *num_occurrences;

  ALIGNED quad_descriptor *quad_descriptors;

  ALIGNED vertex_set *allowed_neighbors;
  ALIGNED vertex_set *known_neighbors;
  ALIGNED vertex_set *known_diagonals;
  ALIGNED vertex_set *known_quad_diagonals;

  ALIGNED vertex_set domains[4];
  uint32_t bound_mask;

  vertex_set finished_vertices;
  vertex_set even_vertices, odd_vertices;

  ALIGNED vertex_index *no_goods;
  size_t no_goods_size, no_goods_capacity;

  ALIGNED uint8_t *symmetry_data;

  ALIGNED vertex_index *boundary_group;
  size_t num_symmetries;
  uint32_t num_boundary_vertices;

  void *ub_data;

  solver_callback cb;
  void *user_data;
} solver;

/**
 * Allocates data to find hexahedral meshes.
 *
 * @param mesh The mesh to manipulate. Pointers within this data structure will
 *   be used while the solver tries to fill them, and therefore the mesh must be
 *   released after the solver.
 * @param num_vertices Maximum number of vertices in a solution. This value
 *   includes both boundary and internal vertices.
 * @param flags Bitfield with options to speed up special cases of the
 *   search. See @ref solver_flags.
 */
error_code solver_init(solver *solver, mesh *mesh, uint32_t num_vertices,
                       uint8_t flags);

/**
 * Releases all memory used by a solver.
 */
void solver_release(solver *solver);

/**
 * Initializes a copy of a solver.
 *
 * This copy can be used independently of the other solver. This includes using
 * both solvers in parallel.
 *
 * Such a copy must be released with @ref solver_release_copy, not @ref
 * solver_release.
 */
error_code solver_init_copy(struct solver *solver, const struct solver *other);

/**
 * Releases all memory used by a solver initialized using @ref solver_init_copy.
 */
void solver_release_copy(solver *solver);

void solver_copy(struct solver *solver, const struct solver *other);

/**
 * Prints debugging information about a solver.
 *
 * @param num_hexes Number of completed hexahedra
 * @param header C-style string printed to give context about how this state was
 *   obtained.
 */
void solver_print(solver *solver, uint32_t num_hexes, const char *header);

typedef struct solver_pool {
  void *data;
  uint32_t element_size, capacity;
} solver_pool;

error_code solver_pool_alloc(solver_pool *pool,
                             uint32_t num_vertices,
                             uint32_t num_quads,
                             uint32_t num_hexes,
                             uint32_t num_symmetries,
                             uint32_t num_boundary_vertices,
                             uint8_t flags,
                             uint32_t n);
error_code solver_pool_init(solver_pool *pool, const struct solver *template,
                            uint32_t n);
void solver_pool_release(solver_pool *pool);

error_code solver_pool_realloc(solver_pool *pool, uint32_t n);

static inline
solver *solver_pool_get(solver_pool *pool, size_t i) {
  return (solver*)((char*)pool->data + pool->element_size*i);
}

/**
 * Loads the set of symmetries from a file.
 *
 * @param [out] vertices Array of permutations in the symmetry group. Should be
 *   freed by the caller.
 * @param [out] num_boundary_vertices Number of vertices per permutation
 * @param [out] num_symmetries Number of permutations in the symmetries group
 */
error_code read_symmetries(const char *path, vertex_index **vertices,
                           vertex_index *num_boundary_vertices,
                           size_t *num_symmetries);

/**
 * @}
 */

#endif
